/*************************************************************/
/*                                                           */
/*     MC823 - Laboratorio de Teleprocessamento e Redes      */
/*     1 Semestre / 2013                                     */
/*     Projeto 1 - Cliente e Servidor UDP de uma Livraria    */
/*                                                           */
/*     Alunos: Joao Gabriel M.D.Mendes - RA 102788           */
/*             Lucas Domingues Lima    - RA 103140           */
/*                                                           */
/*************************************************************/

/* Arquivo: /cliente/udp/cliente.c
   Definicao: representa o cliente em si, e o que sera
              executado de fato e chamara as demais rotinas.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "../../sendrecv.h"
#include "../requisicoes.h"
#include <time.h>

#define SERVERPORT "4950"	// the port users will be connecting to
/* NANO_PER_SECOND define o numero de nanosegundos em um segundo */
#define NANO_PER_SECOND	1000000000

int main(int argc, char *argv[])
{
	int sockfd, numbytes,tam,opcao,n,contador[6],out;
	struct addrinfo hints, *servinfo, *p;
	int rv;
	char *buf,*aux,**temp,id[100],arquivoLog[50],arquivoTempo[50],*req;
	FILE *arqLog,*arqTempo;
	struct timespec tstart;
	struct timespec tstop;
	float driftnano;

	if (argc != 3) {
		fprintf(stderr,"usage: cliente hostname id(8 chars)\n");
		exit(1);
	}
	
	/*inicializa socket UDP*/
	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_DGRAM;

	if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) {
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
		return 1;
	}

	// loop through all the results and make a socket
	for(p = servinfo; p != NULL; p = p->ai_next) {
		if ((sockfd = socket(p->ai_family, p->ai_socktype,
				p->ai_protocol)) == -1) {
			perror("cliente: socket");
			continue;
		}

		break;
	}

	if (p == NULL) {
		fprintf(stderr, "cliente: failed to bind socket\n");
		return 2;
	}

    /*inicializa arquivos de log*/
    sprintf(arquivoLog,"log/%s",argv[2]);
    sprintf(arquivoTempo,"tempo/%s",argv[2]);
    arqLog = fopen(arquivoLog,"a"); //abre arquivo de log do cliente
    arqTempo = fopen(arquivoTempo,"a"); //abre arquivo de tempo do cliente
    temp = carregaArquivo("../ISBNs.txt",&tam); //abre arquivo de IBNS's

    //pega id do cliente
    numbytes = strlen(argv[2]);
    strcpy(id,argv[2]);
    id[numbytes++] = '*';
    id[numbytes] = '\0';
  
    /*inicializa contador de requisicoes, para garantir 100
     * requisicoes de cada tipo */
    for(n=0;n<6;n++)
        contador[n] = 0;
	n = 0;
    srandom(time(NULL));

    while(n < 600)
    {   
	/*escolhe uma opcao aleatoriamente*/
	opcao = random()%6 + 1;
        if(contador[opcao-1] < 100)
        {
            aux = retornaRequisicao(opcao,temp,tam);
            fprintf(arqLog,"Cliente: %s\n",aux);
            numbytes = strlen(argv[2])+strlen(aux);
            req = (char*) malloc(sizeof(char)*(numbytes+1));
            sprintf(req,"%s%s",argv[2],aux);
            /* Primeiro, obtenho o tempo inicial. */
	    clock_gettime(CLOCK_REALTIME, &tstart);
	    //Manda requisicao para o servidor
            if (sendallto(sockfd, req, &numbytes, p->ai_addr, p->ai_addrlen) == -1)
		perror("sendallto");
            //Recebe a resposta do servidor
            out = recvallfrom(sockfd,&buf,&numbytes,NULL,NULL);
	    if (out == -1)
            {
		perror("recvallfrom");
	    }
	    /*trata caso em que houve timeout, fazendo requisicao novamente*/
            else if(out == -2)
            {
                while(out == -2)
                {
                    fprintf(arqLog,"Servidor: timeout, tentando outra vez\n");
                    if (sendallto(sockfd, req, &numbytes, p->ai_addr, p->ai_addrlen) == -1)
		                perror("sendallto");
                    else
                        out = recvallfrom(sockfd,&buf,&numbytes,NULL,NULL);
                }
            }
                    
            fprintf(arqLog,"Servidor: %s\n",buf);
            /* Para obter o tempo final */
	    clock_gettime(CLOCK_REALTIME, &tstop);
            /* Calcula-se o tempo gasto */
	    driftnano = (float)(tstop.tv_sec - tstart.tv_sec);
	    driftnano += (tstop.tv_nsec - tstart.tv_nsec)/(float)NANO_PER_SECOND;
	    /* Grava no arquivo */
	    fprintf(arqTempo,"%d %.9f\n",opcao,driftnano);

	    /*libera memoria utilizada*/
            free(aux);
            free(buf);
            free(req);
	    /*atualiza contador da opcao escolhida*/
	    contador[opcao-1]++;
            n++;
        }
    }


    freeaddrinfo(servinfo);
    close(sockfd);

    return 0;
}
